package copyengine.ui.display.s3
{
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;

    import starling.core.RenderSupport;
    import starling.display.DisplayObject;
    import starling.display.Image;
    import starling.display.QuadBatch;
    import starling.display.Sprite;
    import starling.textures.Texture;
    import starling.textures.TextureSmoothing;
    import starling.utils.MatrixUtil;

    public class Scale3Image extends Sprite
    {
        private static const HELPER_MATRIX:Matrix = new Matrix();
        private static const HELPER_POINT:Point = new Point();
        private static var helperImage:Image;

        private var mBatch:QuadBatch;
        private var mTextures:Scale3Textures;
        private var mHitArea:Rectangle;
        private var mWidth:Number = NaN;
        private var mHeight:Number = NaN;
        private var mFrame:Rectangle;
        private var mTextureScale:Number = 1;
        private var mIsNeedRender:Boolean = true;


        public function Scale3Image(_textures:Scale3Textures) {
            this.textures = _textures;
            this.mBatch = new QuadBatch();
            this.mBatch.touchable = false;
            this.addChild(this.mBatch);

            this.mHitArea = new flash.geom.Rectangle();
            readjustSize();
        }

        /**
         * Readjusts the dimensions of the image according to its current
         * textures. Call this method to synchronize image and texture size
         * after assigning textures with a different size.
         */
        public function readjustSize():void {
            this.width = this.mFrame.width * this.mTextureScale;
            this.height = this.mFrame.height * this.mTextureScale;
            mIsNeedRender = true;
        }

        override public function render(support:RenderSupport, parentAlpha:Number):void {
            doRender();
            super.render(support, parentAlpha);
        }


        override public function hitTest(localPoint:Point, forTouch:Boolean = false):DisplayObject {
            if (forTouch && (!this.visible || !this.touchable)) {
                return null;
            }
            return this.mHitArea.containsPoint(localPoint) ? this : null;
        }

        private function doRender():void {
            if (!mIsNeedRender) {
                return;
            }
            mIsNeedRender = false;
            this.mBatch.reset();

            if (!helperImage) {
                //because Scale3Textures enforces infoTree, we know for sure that
                //this texture will have a size greater than zero, so there
                //won't be an error from Quad.
                helperImage = new Image(this.mTextures.second);
            }
            helperImage.smoothing = TextureSmoothing.BILINEAR;
            helperImage.color = 0xffffff;

            var image:Image;
            if (this.mTextures.direction == Scale3Textures.DIRECTION_VERTICAL) {
                var scaledOppositeEdgeSize:Number = this.mWidth;
                var oppositeEdgeScale:Number = scaledOppositeEdgeSize / this.mFrame.width;
                var scaledFirstRegionSize:Number = this.mTextures.firstRegionSize * oppositeEdgeScale;
                var scaledThirdRegionSize:Number = (this.mFrame.height - this.mTextures.firstRegionSize - this.mTextures.secondRegionSize) * oppositeEdgeScale;
                var sumFirstAndThird:Number = scaledFirstRegionSize + scaledThirdRegionSize;
                if (sumFirstAndThird > this.mHeight) {
                    var distortionScale:Number = (this.mHeight / sumFirstAndThird);
                    scaledFirstRegionSize *= distortionScale;
                    scaledThirdRegionSize *= distortionScale;
                    sumFirstAndThird = scaledFirstRegionSize + scaledThirdRegionSize;
                }
                var scaledSecondRegionSize:Number = this.mHeight - sumFirstAndThird;

                if (scaledOppositeEdgeSize > 0) {
                    if (scaledFirstRegionSize > 0) {
                        helperImage.texture = this.mTextures.first;
                        helperImage.readjustSize();
                        helperImage.x = 0;
                        helperImage.y = 0;
                        helperImage.width = scaledOppositeEdgeSize;
                        helperImage.height = scaledFirstRegionSize;
                        this.mBatch.addImage(helperImage);
                    }

                    if (scaledSecondRegionSize > 0) {
                        helperImage.texture = this.mTextures.second;
                        helperImage.readjustSize();
                        helperImage.x = 0;
                        helperImage.y = scaledFirstRegionSize;
                        helperImage.width = scaledOppositeEdgeSize;
                        helperImage.height = scaledSecondRegionSize;
                        this.mBatch.addImage(helperImage);
                    }

                    if (scaledThirdRegionSize > 0) {
                        helperImage.texture = this.mTextures.third;
                        helperImage.readjustSize();
                        helperImage.x = 0;
                        helperImage.y = this.mHeight - scaledThirdRegionSize;
                        helperImage.width = scaledOppositeEdgeSize;
                        helperImage.height = scaledThirdRegionSize;
                        this.mBatch.addImage(helperImage);
                    }
                }
            }
            else //horizontal
            {
                scaledOppositeEdgeSize = this.mHeight;
                oppositeEdgeScale = scaledOppositeEdgeSize / this.mFrame.height;
                scaledFirstRegionSize = this.mTextures.firstRegionSize * oppositeEdgeScale;
                scaledThirdRegionSize = (this.mFrame.width - this.mTextures.firstRegionSize - this.mTextures.secondRegionSize) * oppositeEdgeScale;
                sumFirstAndThird = scaledFirstRegionSize + scaledThirdRegionSize;
                if (sumFirstAndThird > this.mWidth) {
                    distortionScale = (this.mWidth / sumFirstAndThird);
                    scaledFirstRegionSize *= distortionScale;
                    scaledThirdRegionSize *= distortionScale;
                    sumFirstAndThird = scaledFirstRegionSize + scaledThirdRegionSize;
                }
                scaledSecondRegionSize = this.mWidth - sumFirstAndThird;

                if (scaledOppositeEdgeSize > 0) {
                    if (scaledFirstRegionSize > 0) {
                        helperImage.texture = this.mTextures.first;
                        helperImage.readjustSize();
                        helperImage.x = 0;
                        helperImage.y = 0;
                        helperImage.width = scaledFirstRegionSize;
                        helperImage.height = scaledOppositeEdgeSize;
                        this.mBatch.addImage(helperImage);
                    }

                    if (scaledSecondRegionSize > 0) {
                        helperImage.texture = this.mTextures.second;
                        helperImage.readjustSize();
                        helperImage.x = scaledFirstRegionSize;
                        helperImage.y = 0;
                        helperImage.width = scaledSecondRegionSize;
                        helperImage.height = scaledOppositeEdgeSize;
                        this.mBatch.addImage(helperImage);
                    }

                    if (scaledThirdRegionSize > 0) {
                        helperImage.texture = this.mTextures.third;
                        helperImage.readjustSize();
                        helperImage.x = this.mWidth - scaledThirdRegionSize;
                        helperImage.y = 0;
                        helperImage.width = scaledThirdRegionSize;
                        helperImage.height = scaledOppositeEdgeSize;
                        this.mBatch.addImage(helperImage);
                    }
                }
            }

        }

        /**
         * @private
         */
        public override function getBounds(targetSpace:DisplayObject, resultRect:Rectangle = null):Rectangle {
            if (!resultRect) {
                resultRect = new Rectangle();
            }

            var minX:Number = Number.MAX_VALUE, maxX:Number = -Number.MAX_VALUE;
            var minY:Number = Number.MAX_VALUE, maxY:Number = -Number.MAX_VALUE;

            if (targetSpace == this) // optimization
            {
                minX = this.mHitArea.x;
                minY = this.mHitArea.y;
                maxX = this.mHitArea.x + this.mHitArea.width;
                maxY = this.mHitArea.y + this.mHitArea.height;
            }
            else {
                this.getTransformationMatrix(targetSpace, HELPER_MATRIX);

                MatrixUtil.transformCoords(HELPER_MATRIX, this.mHitArea.x, this.mHitArea.y, HELPER_POINT);
                minX = minX < HELPER_POINT.x ? minX : HELPER_POINT.x;
                maxX = maxX > HELPER_POINT.x ? maxX : HELPER_POINT.x;
                minY = minY < HELPER_POINT.y ? minY : HELPER_POINT.y;
                maxY = maxY > HELPER_POINT.y ? maxY : HELPER_POINT.y;

                MatrixUtil.transformCoords(HELPER_MATRIX, this.mHitArea.x, this.mHitArea.y + this.mHitArea.height, HELPER_POINT);
                minX = minX < HELPER_POINT.x ? minX : HELPER_POINT.x;
                maxX = maxX > HELPER_POINT.x ? maxX : HELPER_POINT.x;
                minY = minY < HELPER_POINT.y ? minY : HELPER_POINT.y;
                maxY = maxY > HELPER_POINT.y ? maxY : HELPER_POINT.y;

                MatrixUtil.transformCoords(HELPER_MATRIX, this.mHitArea.x + this.mHitArea.width, this.mHitArea.y, HELPER_POINT);
                minX = minX < HELPER_POINT.x ? minX : HELPER_POINT.x;
                maxX = maxX > HELPER_POINT.x ? maxX : HELPER_POINT.x;
                minY = minY < HELPER_POINT.y ? minY : HELPER_POINT.y;
                maxY = maxY > HELPER_POINT.y ? maxY : HELPER_POINT.y;

                MatrixUtil.transformCoords(HELPER_MATRIX, this.mHitArea.x + this.mHitArea.width, this.mHitArea.y + this.mHitArea.height, HELPER_POINT);
                minX = minX < HELPER_POINT.x ? minX : HELPER_POINT.x;
                maxX = maxX > HELPER_POINT.x ? maxX : HELPER_POINT.x;
                minY = minY < HELPER_POINT.y ? minY : HELPER_POINT.y;
                maxY = maxY > HELPER_POINT.y ? maxY : HELPER_POINT.y;
            }

            resultRect.x = minX;
            resultRect.y = minY;
            resultRect.width = maxX - minX;
            resultRect.height = maxY - minY;

            return resultRect;
        }


        public function get textures():Scale3Textures {
            return this.mTextures;
        }

        public function set textures(value:Scale3Textures):void {
            this.mTextures = value;
            var texture:Texture = this.mTextures.texture;
            this.mFrame = texture.frame;
            if (!this.mFrame) {
                this.mFrame = new Rectangle(0, 0, texture.width, texture.height);
            }
            mIsNeedRender = true;
        }


        override public function get width():Number {return this.mWidth;}

        override public function set width(value:Number):void {
            if (this.mWidth == value) {
                return;
            }
            this.mWidth = this.mHitArea.width = value;
            mIsNeedRender = true;
        }

        override public function get height():Number {return this.mHeight;}

        override public function set height(value:Number):void {
            if (this.mHeight == value) {
                return;
            }
            this.mHeight = this.mHitArea.height = value;
            mIsNeedRender = true;
        }


    }
}
